package org.pathlet.core.config;

import java.util.Collection;
import java.util.Map;

import org.pathlet.core.config.convert.CollectionFactory;
import org.pathlet.core.config.convert.BeanProperty;
import org.pathlet.core.config.convert.ValueNode;
import org.pathlet.core.config.impl.BeanResourceConfigHandler;
import org.pathlet.core.InstanceSpace;
import org.pathlet.core.PathPattern;
import org.pathlet.core.PathletResource;
import org.pathlet.core.ResourceInstanceEvent;
import org.pathlet.core.ResourceInstanceProcessor;
import org.pathlet.core.BeanResource;
import org.pathlet.util.PathletUtils;

public class ConfigInstanceEvent implements ResourceInstanceEvent {

	public Object instanceTarget(ResourceInstanceProcessor processor, InstanceSpace space, PathletResource resource) {

		Object ret = processor.process(space);

		weaveProperties(space, ret, resource);

		return ret;
	}

	public void flush(String scope, PathPattern matchPattern) {
		//This ResourceInstanceEvent has no cached object in it.
		//Do nothing in this callback method.
	}

	protected void weaveProperties(InstanceSpace space, Object instance, PathletResource resource) {

		if(resource instanceof BeanResource) {
			BeanResource beanResource = (BeanResource)resource;
			BeanResourceConfigHandler configHandler = beanResource.getConfigHandler();
			if(configHandler.getProperties() == null) {
				//ignore for none configuration resource
				return;
			}

			Map<BeanProperty, ValueNode> properties = configHandler.getProperties();

			for( Map.Entry<BeanProperty, ValueNode> entry : properties.entrySet()) {

				BeanProperty prop = entry.getKey();
				ValueNode valueNode = entry.getValue();

				Object propValue = getNodeValue(space, resource, valueNode);

				prop.setProperty(instance, propValue);
			}

		}

	}

	/**
	 * 根据ValueNode的描述信息，从InstanceSpace获取实际的值。
	 * @param space 存放资源的空间
	 * @param resource 资源对象
	 * @param valueNode 值节点的配置信息描述
	 * @return
	 * @throws ConfigException
	 */
	private Object getNodeValue(InstanceSpace space, PathletResource resource, ValueNode valueNode) throws ConfigException {

		Object value;
		switch(valueNode.getType()) {
		case COLLECTION:
			Collection<ValueNode> listNodes = valueNode.getChildren();
			int listSize = listNodes.size();
			Collection collValue = CollectionFactory.createCollection(valueNode.getValueType(), listSize);
			for(ValueNode childNode : listNodes) {
				Object listElement = getNodeValue(space, resource, childNode);
				collValue.add(listElement);
			}
			value = collValue;
			break;
		case MAP:
			Collection<ValueNode> mapNodes = valueNode.getChildren();
			int mapSize = mapNodes.size();
			Map mapValue = CollectionFactory.createMap(valueNode.getValueType(), mapSize);
			for(ValueNode childNode : mapNodes) {
				Object key = childNode.getKey();
				Object element = getNodeValue(space, resource, childNode);
				mapValue.put(key, element);
			}
			value = mapValue;
			break;
		case INSTANCE:
			value = PathletUtils.getInstance((String)valueNode.getValue(), resource, space);
			if(value == null) {
				throw new ConfigException("Failed to get instance by path: '" + (String)valueNode.getValue() + "'");
			}
			break;
		case INSTANCE_TYPE:
			value = PathletUtils.getInstanceByType((String)valueNode.getValue(), space);
			if(value == null) {
				throw new ConfigException("Failed to get instance by type: '" + (String)valueNode.getValue() + "'");
			}
			break;
		case RESOURCE:
			value = PathletUtils.getResource((String)valueNode.getValue(), resource, space);
			if(value == null) {
				throw new ConfigException("Failed to get Resource by path: '" + (String)valueNode.getValue() + "'");
			}
			break;
		case SPACE:
			value = PathletUtils.getSpace((String)valueNode.getValue(), space);
			if(value == null) {
				throw new ConfigException("Failed to get InstanceSpace from the name: '" + (String)valueNode.getValue() + "'");
			}
			break;
		case CONTAINER:
			value = space.getContainer();
			if(value == null) {
				throw new ConfigException("Failed to get PathletContainer instance");
			}
			break;
		case STRING:
		case BOOLEAN:
		case INTEGER:
		case LONG:
		case FLOAT:
		case DOUBLE:
		case PATH:
		case MATCH_PATTERN:
			value = valueNode.getValue();
			break;
		default:
			throw new ConfigException("Not support the value type: " + valueNode.getType());
		}
		return value;
	}


}
